package com.hmall.controller;

import com.alibaba.fastjson.JSON;
import com.hmall.Parameter;
import com.hmall.common.client.ItemClient;
import com.hmall.common.dto.PageDTO;
import com.hmall.common.pojo.Item;
import com.hmall.common.pojo.ItemDoc;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author chengjy
 * @date 2023/5/19
 */
@RestController
@RequestMapping("/search")
@Slf4j
public class SearchController {
    @Autowired
    private RestHighLevelClient client;

    @Autowired
    private ItemClient itemClient;

    @PostMapping("/filters")
    public Map<String,List<String>> groupFilters(@RequestBody Parameter parameter) throws IOException {

        //1、条件校验
        String key = parameter.getKey();
        //2.1、封装查询条件： key，价格区间
        SearchRequest request = new SearchRequest("hamll");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder bool = QueryBuilders.boolQuery();
        //组装DSL参数 如果key为空
        if("".equals(key)){
            bool.must(QueryBuilders.matchAllQuery());
        }else{
            bool.must(QueryBuilders.matchQuery("all",key));
        }
        //2.2、封装查询条件：   品牌，分类，价格区间
        String brand = "brand";
        String category = "category";
        List<String> queryNameList = Arrays.asList(brand,category);
        //品牌
        if (!ObjectUtils.isEmpty(parameter.getBrand())){
            bool.filter(QueryBuilders.termQuery(brand,parameter.getBrand()));
        }
        //分类
        if (!ObjectUtils.isEmpty(parameter.getCategory())){
            bool.filter(QueryBuilders.termQuery(category,parameter.getCategory()));
        }
        //价格
        if(!ObjectUtils.isEmpty(parameter.getMaxPrice())){
            if(!ObjectUtils.isEmpty(parameter.getMinPrice())){
                bool.filter(QueryBuilders.rangeQuery("price").lte(parameter.getMaxPrice()).gte(parameter.getMinPrice()));
            }
        }
        searchSourceBuilder.query(bool);
        //2.3、封装查询条件： 分组属性：品牌，分类   分组条件：前端不论有无传入都做分组
        searchSourceBuilder.size(0);

        aggregation(searchSourceBuilder, brand);
        aggregation(searchSourceBuilder, category);

        request.source(searchSourceBuilder);
        //3、发送请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //4、解析响应
        return  aggregationResponse(response, queryNameList);
    }

    /**
     * 解析分组聚合的response
     * @param response
     * @param queryNameList
     * @return
     */
    private Map<String, List<String>> aggregationResponse(SearchResponse response, List<String> queryNameList) {
        Map<String, List<String>> map = new HashMap<>();
        //1、解析聚合结果
        Aggregations aggregations = response.getAggregations();
        if (ObjectUtils.isEmpty(aggregations)){
            return map;
        }
        //2、根据名称获取聚合结果
        for (String queryName : queryNameList) {
            //3、获取桶
            Terms brandTerms = aggregations.get(queryName + "_ikun");
            if (ObjectUtils.isEmpty(brandTerms)) {
                continue;
            }
            List<? extends Terms.Bucket> buckets = brandTerms.getBuckets();
            //获取key
            List<String> keyList = buckets.stream().map(MultiBucketsAggregation.Bucket::getKeyAsString).collect(Collectors.toList());
            map.put(queryName,keyList);
        }
        return map;
    }

    /**
     * 组装聚合条件
     * @param request
     * @param brand
     */
    private void aggregation(SearchSourceBuilder request, String brand) {
        request.aggregation(AggregationBuilders
                .terms(brand +"_ikun")
                .field(brand));
    }

    @PostMapping("/list")
    public PageDTO<Item> selectList(@RequestBody Parameter parameter) throws IOException {
        //1、封装查询条件： 分页、排序、倒排索引
        String key = parameter.getKey();
        //2、条件校验
        SearchRequest request = new SearchRequest("hamll");
        //组装DSL参数
        //2.1 如果key为空
        BoolQueryBuilder bool = QueryBuilders.boolQuery();
        //组装DSL参数 如果key为空
        if("".equals(key)){
            bool.must(QueryBuilders.matchAllQuery());
        }else{
            bool.must(QueryBuilders.matchQuery("all",key));
        }
        //2.2、封装查询条件：   品牌，分类，价格区间
        String brand = "brand";
        String category = "category";
        //品牌
        if (!ObjectUtils.isEmpty(parameter.getBrand())){
            bool.filter(QueryBuilders.termQuery(brand,parameter.getBrand()));
        }
        //分类
        if (!ObjectUtils.isEmpty(parameter.getCategory())){
            bool.filter(QueryBuilders.termQuery(category,parameter.getCategory()));
        }
        //价格
        if(!ObjectUtils.isEmpty(parameter.getMaxPrice())){
            if(!ObjectUtils.isEmpty(parameter.getMinPrice())){
                bool.filter(QueryBuilders.rangeQuery("price").lte(parameter.getMaxPrice()).gte(parameter.getMinPrice()));
            }
        }
        request.source().query(bool);
        //2.2 如果排序为默认
            String sortBy = parameter.getSortBy();
        if("default".equals(sortBy)){
            request.source().sort("_score", SortOrder.DESC);
        }else{
            request.source().sort(sortBy, SortOrder.DESC);
        }

        //2.3 设置分页
        Integer page = parameter.getPage();
        Integer size = parameter.getSize();
        request.source().from((page -1) * size ).size(size);
        //2.4 设置高亮
        List<String> fieldList = Arrays.asList("name","brand","spec","category");
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        for (String field : fieldList) {
            highlightBuilder.field(field).requireFieldMatch(false);
        }
        request.source().highlighter(highlightBuilder);
        //3、发送请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //4、解析响应
        return   handelResponse(response,fieldList);
    }

    /**
     * 解析List接口响应请求
     * @param response  响应体
     * @param fieldList 高亮参数
     * @return
     */
    private PageDTO<Item> handelResponse(SearchResponse response, List<String> fieldList) {
        PageDTO<Item> pageDTO = new PageDTO<>();
        SearchHits searchHits = response.getHits();
        //4.1 总条数
        pageDTO.setTotal(searchHits.getTotalHits().value);
        //4.2 查询的结果数组
        SearchHit[] hits = searchHits.getHits();
        List<Item> itemList = new ArrayList<>();
        //将item的所有属性和字段Field生成映射map
        Map<String, Field> fieldMap = Arrays.stream(Item.class.getDeclaredFields()).collect(Collectors.toMap(field -> field.getName(), field -> field));
        //4.3 转json实体对象
        for (SearchHit hit : hits) {
            String json = hit.getSourceAsString();
            //反序列化
            Item item = JSON.parseObject(json, Item.class);
            //解析高亮
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            if (!ObjectUtils.isEmpty(highlightFields)){

                for (String fieldName : fieldList) {
                    try {
                        //获取高亮属性的值
                        String value = highlightFields.get(fieldName).getFragments()[0].toString();

                        //使用反射给属性注入值
                        Field field = fieldMap.get(fieldName);
                        field.setAccessible(true);
                        field.set(item,value);

                    } catch (Exception e) {
                    }
                }

            }
            //处理完毕，组装对象
            itemList.add(item);
            pageDTO.setList(itemList);
        }
        return pageDTO;
    }


    @GetMapping
    public String queryOrderById() throws IOException {
        //测试查询文档
        String id = "1";
        String indexName = "heima";
        //1、创建request对象
        GetRequest request = new GetRequest(indexName,id);
        //2、发起请求，得到结果
        GetResponse response = client.get(request, RequestOptions.DEFAULT);
        //3、解析结果
        String json = response.getSourceAsString();
        System.out.println(json);

        return json;
    }

    /**
     * 批量新增索引文档
     */
    @GetMapping("saveList")
    public void save()  {
        List<Item> itemList = itemClient.selectList();
        List<ItemDoc> items = itemList.stream().map(ItemDoc::new).collect(Collectors.toList());
        //使用stream流将数据拆成四份
        //截取
        int batchSize = 10000;
        List<List<ItemDoc>> dividedDataList = new ArrayList<>();
        for (int i = 0; i < items.size(); i += batchSize) {
            int end = Math.min(i + batchSize, items.size());
            dividedDataList.add(items.subList(i, end));
        }
        // 2.准备参数，添加多个新增的Request
        for (List<ItemDoc> itemLists : dividedDataList) {
            //创建请求
            BulkRequest request = new BulkRequest();
            for (ItemDoc item : itemLists) {
                // 2.2.创建新增文档的Request对象
                request.add(new IndexRequest("hamll")
                        .id(item.getId().toString())
                        .source(JSON.toJSONString(item), XContentType.JSON));
            }
            try {
                client.bulk(request, RequestOptions.DEFAULT);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        // 3.发送请求
        System.out.println("批量新增成功");

    }

    @GetMapping("/hello")
    public String hello() {
        Item item = this.itemClient.getbyId(546872L);
        return "帅锅，你好啊:{"+item+"}";
    }
}
